use crate::config::Config;
use crate::errors::*;
use crate::signer::sign;
use reqwest::{Client, Proxy, Response};
use std::collections::HashMap;
use std::time::Duration;

pub struct BinanceRestClient {
    pub config: Config,
    pub client: Client,
}

impl BinanceRestClient {
    pub fn new(config: Config) -> Self {
        let mut builder = Client::builder().timeout(Duration::from_secs(30));

        // ✅ 设置代理（如果有）
        if let Some(proxy_url) = &config.proxy {
            if let Ok(proxy) = Proxy::all(proxy_url) {
                builder = builder.proxy(proxy);
                println!("[REST] Using proxy: {}", proxy_url);
            }
        }

        let client = builder.build().unwrap();
        Self { config, client }
    }

    // 统一处理 HTTP 响应
    async fn handle_response(&self, resp: Response) -> Result<String> {
        let status = resp.status();
        let text = resp.text().await.unwrap_or_default();

        if !status.is_success() {
            if let Ok(api_err) = serde_json::from_str::<BinanceApiErrorResponse>(&text) {
                return Err(BinanceError::ApiError(api_err));
            }
            return Err(BinanceError::Other(format!(
                "HTTP {} Error with body: {}",
                status, text
            )));
        }

        Ok(text)
    }

    pub async fn get(&self, endpoint: &str, signed: bool) -> Result<String> {
        let mut url = format!("{}{}", self.config.rest_base_url(), endpoint);
        if signed {
            let ts = chrono::Utc::now().timestamp_millis();
            let query = format!("timestamp={}", ts);
            let signature = sign(&self.config.secret_key, &query);
            url = format!("{}?{}&signature={}", url, query, signature);
        }
        let resp = self
            .client
            .get(&url)
            .header("X-MBX-APIKEY", &self.config.api_key)
            .send()
            .await?;

        self.handle_response(resp).await
    }

    /// 带参数 POST 方法（支持签名）
    pub async fn post(&self, endpoint: &str, params: &HashMap<&str, String>, signed: bool) -> Result<String> {
        let mut query = params
            .iter()
            .map(|(k, v)| format!("{}={}", k, v))
            .collect::<Vec<String>>()
            .join("&");

        if signed {
            let ts = chrono::Utc::now().timestamp_millis();
            if !query.is_empty() {
                query.push('&');
            }
            query.push_str(&format!("timestamp={}", ts));
            let signature = sign(&self.config.secret_key, &query);
            query.push_str(&format!("&signature={}", signature));
        }

        let url = format!("{}{}", self.config.rest_base_url(), endpoint);
        let resp = self
            .client
            .post(&url)
            .header("X-MBX-APIKEY", &self.config.api_key)
            .header("Content-Type", "application/x-www-form-urlencoded")
            .body(query)
            .send()
            .await?;

        self.handle_response(resp).await
    }

    pub async fn put(&self, endpoint: &str, signed: bool) -> Result<String> {
        let mut url = format!("{}{}", self.config.rest_base_url(), endpoint);
        if signed {
            let ts = chrono::Utc::now().timestamp_millis();
            let query = format!("timestamp={}", ts);
            let signature = sign(&self.config.secret_key, &query);
            url = format!("{}?{}&signature={}", url, query, signature);
        }
        let req = self.client.put(&url)
            .header("X-MBX-APIKEY", &self.config.api_key);
        let res = req.send().await?.text().await?;
        Ok(res)
    }

    pub async fn delete(&self, endpoint: &str, signed: bool) -> Result<String> {
        let mut url = format!("{}{}", self.config.rest_base_url(), endpoint);
        if signed {
            let ts = chrono::Utc::now().timestamp_millis();
            let query = format!("timestamp={}", ts);
            let signature = sign(&self.config.secret_key, &query);
            url = format!("{}?{}&signature={}", url, query, signature);
        }
        let res = self
            .client
            .delete(&url)
            .header("X-MBX-APIKEY", &self.config.api_key)
            .send()
            .await?
            .text()
            .await?;
        Ok(res)
    }
}
